//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Ext.h".
//-------------------------------------------------------------------

#ifndef _UXS_PROPERTY_TYPE_H
#define _UXS_PROPERTY_TYPE_H

#pragma once

#include "../../stl_core/ux_stl_core.h"

namespace UX_STL
{
namespace UXTypesExt
{
	using namespace UXTypes;

	
	//
	// Setter (for read only property)
	//

	template <typename T, typename C>
	struct TPropertySetter_ReadOnly
	{
		static void Set (const T& val, C *ptr)					{ STATIC_WARNING( "invalid operation" ); }
		static void Set (usize index, const T& val, C *ptr)		{ STATIC_WARNING( "invalid operation" ); }
	};


	//
	// Getter (for write only property)
	//

	template <typename T, typename C>
	struct TPropertyGetter_WriteOnly
	{
		static T const & Get (C const *ptr)						{ STATIC_WARNING( "invalid operation" ); }
		static T const & Get (usize index, C const *ptr)		{ STATIC_WARNING( "invalid operation" ); }
	};



	//
	// Property
	//

	template <	typename T,		// value type
				typename C,		// class type
				typename S		= TPropertySetter_ReadOnly< T, C >,
				typename G		= TPropertyGetter_WriteOnly< T, C >
			 >
	struct TProperty
	{
	// types
	public:
		struct  TPropertyPtr;
		struct  TPropertyIdxRef;
		struct  TPropertyIdxPtr;

		typedef TProperty< T, C, S, G >		Self;
		typedef TPropertyPtr				ptr_t;
		typedef TPropertyIdxRef				iref_t;
		typedef S							Setter;
		typedef G							Getter;
		typedef isize						offset_t;


	// variables
	private:
		static const offset_t	_s_iOffset;


	// methods
	private:
		C *			_GetPtr ()						{ return MovePointer< C *>( this, _s_iOffset ); }
		C const *	_GetPtr ()	const				{ return MovePointer< const C *>( this, _s_iOffset ); }

	public:
		TProperty()									{}


		// set/get
		Self &		set (const T& val)				{ Setter::Set( val, _GetPtr() );     return *this; }
		Self &		set (usize i, const T& val)		{ Setter::Set( i, val, _GetPtr() );  return *this; }
		T const 	get () const					{ return Getter::Get( _GetPtr() ); }
		T const 	get (usize i) const				{ return Getter::Get( i, _GetPtr() ); }
		iref_t		get (usize i)					{ return iref_t( *this, i ); }


		// type cast
		operator T		()	const					{ return get(); }


		// operators
		Self &		operator =  (const T& val)		{ return set( val ); }
		Self &		operator =  (const Self& r)		{ return set( (T)r ); }
		ptr_t		operator &  ()					{ return ptr_t( *this ); }
		iref_t		operator [] (usize i)			{ return get( i ); }
		T const 	operator [] (usize i) const		{ return get( i ); }

		
	// types
	public:
		struct TPropertyIdxRef
		{
		// types
		public:
			typedef TPropertyIdxRef					Self;
			typedef TProperty< T, C, S, G >			property_t;
			typedef TPropertyIdxPtr					iptr_t;
		

		// variables
		private:
			property_t &	_prop;
			const usize		_i;


		// methods
		public:
			TPropertyIdxRef (property_t &prop, usize i): _prop(prop), _i(i) {}

			operator T () const						{ return _prop.get(_i); }

			Self & operator = (const T& val)		{ _prop.set( _i, val );   return *this; }
			Self & operator = (const Self& r)		{ _prop.set( _i, (T)r );  return *this; }

			iptr_t	operator & ()					{ return TPropertyIdxPtr( _prop, _i ); }
		};


		struct TPropertyPtr
		{
		// types
		public:
			typedef TPropertyPtr				Self;
			typedef TProperty< T, C, S, G >		property_t;
			

		// variables
		private:
			property_t	&_prop;
			T			_val;


		// methods
		public:
			TPropertyPtr (property_t &prop): _prop(prop), _val(_prop.get()) {}
			~TPropertyPtr ()		{ _prop.set( _val ); }

			operator T * ()			{ return &_val; }
			operator T & ()			{ return _val; }
			operator T   () const	{ return _val; }
		};


		struct TPropertyIdxPtr
		{
		// types
		public:
			typedef TPropertyIdxPtr				Self;
			typedef TProperty< T, C, S, G >		property_t;
			

		// variables
		private:
			property_t &	_prop;
			T				_val;
			const usize		_i;


		// methods
		public:
			TPropertyIdxPtr (property_t &prop, usize i): _prop(prop), _val(_prop.get()), _i(i) {}
			~TPropertyIdxPtr ()		{ _prop.set( _i, _val ); }

			operator T * ()			{ return &_val; }
			operator T & ()			{ return _val; }
			operator T   () const	{ return _val; }
		};
	};	
	

	template <typename T, typename C, typename S, typename G>
	const typename TProperty<T,C,S,G>::offset_t	TProperty<T,C,S,G>::_s_iOffset = 0;

	
#	define INIT_PROPERTY( _prop_type, _prop_ptr ) \
		const _prop_type::offset_t	_prop_type::_s_iOffset	= -ClassFieldOffset( _prop_ptr )

#	define PROPERTY_DECL( _class, _name, _val_type, _set, _get ) \
	private: \
		friend struct __##_name##_SetterGetter; \
		struct __##_name##_SetterGetter { \
			static _val_type const & Get (_class const *pthis)		{ return pthis->_get(); } \
			static void Set (const _val_type& val, _class *pthis)	{ pthis->_set( val ); } \
		}; \
		typedef TProperty< _val_type, _class, __##_name##_SetterGetter, __##_name##_SetterGetter > _property##_name##_t; \
		\
	public: \
		_property##_name##_t	_name;

	/*
		example:

		class A
		{
		private:
			int	_i;
			
			friend struct TSetter;
			friend struct TGetter;

			struct TSetter { static void Set(int i, A* ptr)	{ ptr->_i = i; } };
			struct TGetter { static int Get(A const *ptr)	{ return ptr->_i; } };
			typedef TProperty< int, A, TSetter, TGetter >	property_t;

		public:
			property_t	index;
		};

		// in *.cpp file
		INIT_PROPERTY( A::property_t, &A::index );


		// use as function out argument

		void func1(int *i) {...}
		void func2(int &i) {...}

		A	a;

		func1( &a.index );
		func2( &a.index );
		func1( &a.array[2] );
		func2( &a.array[3] );
	*/

//-------------------------------------------------------------------

}	// UXTypesExt
}	// UX_STL

#endif	// _UXS_PROPERTY_TYPE_H